package com.sjl.filter;


import cn.hutool.core.util.ArrayUtil;
import cn.hutool.log.StaticLog;
import com.sjl.springboot.domain.CommonResponse;
import com.sjl.springboot.domain.enums.CommonCode;
import com.sjl.springboot.domain.user.SysUser;
import com.sjl.springboot.exception.DiyException;
import com.sjl.utils.SimpleFactoryCache;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author sjl
 * @version V1.0
 * @date 2018/12/18 0018 14:56
 */
@Aspect
@Component
public class ServiceMonitor {

    private final static String[] TYPES = {"java.lang.Integer", "java.lang.Double",
            "java.lang.Float", "java.lang.Long", "java.lang.Short",
            "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
            "java.lang.String", "int", "double", "long", "short", "byte",
            "boolean", "char", "float"};
    private final static String KEYWORD = "userId";


    @Around("execution(public * com.sjl.impl..*ServiceImpl.*(..))")
    public Object logServiceAccess(ProceedingJoinPoint joinPoint)
            throws Throwable {

        StaticLog.info("Completed: joinPoint:{}" + joinPoint);
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            Object reqObj = args[0];
            // 字段校验
            filedVerify(reqObj);
        }

        Object proceed;
        try {
            annotationVerify(joinPoint);
            proceed = joinPoint.proceed();

        } catch (DiyException e) {
            StaticLog.error(e.getMessage());
            CommonResponse response = new CommonResponse();
            response.setCode(e.getErrCode());
            response.setMsg(e.getErrReason());
            return response;
        }
        return proceed;
    }

    /**
     * 判断有没有自定义注解
     *
     * @param joinPoint ProceedingJoinPoint
     */
    private void annotationVerify(ProceedingJoinPoint joinPoint) throws DiyException{
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        //获取所有的参数
        if (targetMethod.isAnnotationPresent(Demo.class)) {
            StaticLog.info("这个方法有 Demo 注解");
            Object[] args = joinPoint.getArgs();
            Map<String, String> params = new HashMap<>(20);
            for (Object arg : args) {
                // 获取对象类型
                String typeName = arg.getClass().getTypeName();
                for (String t : TYPES) {
                    //1 判断是否是基础类型
                    if (t.equals(typeName)) {
                        StaticLog.info("请求参数位基础类型");
                    } else {
                        //2 通过反射获取实体类属性
                        params = getFieldsValue(arg);
                    }
                }
            }
            StaticLog.info(params.toString());
            if (params.containsKey(KEYWORD) && StringUtils.isNotBlank(params.get(KEYWORD))) {
                String value = targetMethod.getAnnotation(Demo.class).value();
                if (StringUtils.equals(value, Demo.findclose)) {
                    StaticLog.info("注解传值 查看开闭市:{}", Demo.findclose);
                } else if (StringUtils.equals(value, Demo.findStatus)) {
                    StaticLog.info("注解传值 查看合同状态 :{}", Demo.findclose);
                } else {
                    StaticLog.info("注解传值 默认状态 :{}", value);
                    throw  new DiyException(CommonCode.Update_false);
                }
                StaticLog.info("AOP参数获取参数 userId:{},StaticLoginName:{}", params.get("userId"), params.get("userName"));

            }

        }

    }

    private static Map<String, String> getFieldsValue(Object obj) {
        //通过反射获取所有的字段，getFileds()获取public的修饰的字段
        //getDeclaredFields获取private protected public修饰的字段
        Field[] fields = SimpleFactoryCache.getFiles(obj.getClass());
        String typeName = obj.getClass().getTypeName();
        //获取所有父类
        List<Class<?>> classes = SimpleFactoryCache.getsuperClass(obj.getClass());
        Map<String, String> result = new HashMap<>(20);
        if(ArrayUtil.contains(TYPES,typeName)){
            return result;
        }

        forField(obj,fields,result);
        //根据父类属性获取值
        classes.forEach(c->forField(obj,c.getDeclaredFields(),result));

        return result;
    }

    private static void forField(Object obj,Field[] fields,Map<String, String> result){
        for (Field f : fields) {
            //在反射时能访问私有变量
            f.setAccessible(true);
            try {
                for (String str : TYPES) {
                    //这边会有问题，如果实体类里面继续包含实体类，这边就没法获取。
                    //其实，我们可以通递归的方式去处理实体类包含实体类的问题。
                    if (f.getType().getName().equals(str)) {

                        result.put(f.getName(), f.get(obj) + "");
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据 注解字段校验
     *
     * @param reqObj 请求参数
     * @throws BusinessException 异常
     */
    private void filedVerify(final Object reqObj) throws BusinessException {
        if (reqObj == null) {
            return;
        }
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        javax.validation.Validator validator = factory.getValidator();
        Set<ConstraintViolation<Object>> validate = validator.validate(reqObj,
                Default.class);
        StringBuilder sb = new StringBuilder();
        sb.append(" 字段校验错误 ——");
        for (ConstraintViolation<Object> cv : validate) {
            sb.append(cv.getPropertyPath());
            sb.append(",");
            sb.append(cv.getInvalidValue());
            sb.append(":");
            sb.append(cv.getMessage());
            sb.append(";");
        }
        if (validate.size() > 0) {
            String fieldLessError = sb.toString();
            StaticLog.error("fieldLessError :{}", fieldLessError);
            throw new BusinessException(fieldLessError, "500");
        }
    }


    public static void main(String[] args) {
        SimpleFactoryCache.getFiles(SysUser.class);
    }


}
